using System;
using System.Collections.Generic;
using System.Text;

namespace PerlinNoise.NoiseGenerator
{
    public class IndexTableNoiseGenerator : AbstractNoiseGenerator
    {
        private static readonly int PERMUTATION_TABLE_SIZE;
        private static readonly int[] PERMUTATION_TABLE;
        private static readonly double[] VALUES_TABLE;

        static IndexTableNoiseGenerator()
        {
            PERMUTATION_TABLE_SIZE = 256;

            Random intRand = new Random(42);
            Random doubleRand = new Random(intRand.Next());

            bool[] tempPermutationTable = new bool[PERMUTATION_TABLE_SIZE];
            PERMUTATION_TABLE = new int[PERMUTATION_TABLE_SIZE * 2];
            VALUES_TABLE = new double[PERMUTATION_TABLE_SIZE];

            for (int i = 0; i < PERMUTATION_TABLE_SIZE; ++i) {
                int value;
                do {
                    value = intRand.Next() % PERMUTATION_TABLE_SIZE;
                    if (!tempPermutationTable[value]) {
                        tempPermutationTable[value] = true;
                        PERMUTATION_TABLE[i] = PERMUTATION_TABLE[i + PERMUTATION_TABLE_SIZE] = value;
                    }
                    VALUES_TABLE[i] = doubleRand.NextDouble() * 2.0d - 1.0d;
                } while (!tempPermutationTable[value]);
            }
        }

        public override double noise1D(int x)
        {
            if (x > 0) {
                x = x & (PERMUTATION_TABLE_SIZE - 1);
            } else {
                x = 255 - (-x & (PERMUTATION_TABLE_SIZE - 1));
            }
            return VALUES_TABLE[PERMUTATION_TABLE[x]];
        }

        public override double noise2D(int x, int y)
        {
            if (x > 0) {
                x = x & (PERMUTATION_TABLE_SIZE - 1);
            } else {
                x = 255 - (-x & (PERMUTATION_TABLE_SIZE - 1));
            }
            if (y > 0) {
                y = y & (PERMUTATION_TABLE_SIZE - 1);
            } else {
                y = 255 - (-y & (PERMUTATION_TABLE_SIZE - 1));
            }
            return VALUES_TABLE[PERMUTATION_TABLE[x + PERMUTATION_TABLE[y]]];
        }

        public override double noise3D(int x, int y, int z)
        {
            if (x > 0) {
                x = x & (PERMUTATION_TABLE_SIZE - 1);
            } else {
                x = 255 - (-x & (PERMUTATION_TABLE_SIZE - 1));
            }
            if (y > 0) {
                y = y & (PERMUTATION_TABLE_SIZE - 1);
            } else {
                y = 255 - (-y & (PERMUTATION_TABLE_SIZE - 1));
            }
            if (z > 0) {
                z = z & (PERMUTATION_TABLE_SIZE - 1);
            } else {
                z = 255 - (-z & (PERMUTATION_TABLE_SIZE - 1));
            }
            return VALUES_TABLE[PERMUTATION_TABLE[x + PERMUTATION_TABLE[y + PERMUTATION_TABLE[z]]]];
        }

        public override double noise4D(int x, int y, int z, int t)
        {
            if (x > 0) {
                x = x & (PERMUTATION_TABLE_SIZE - 1);
            } else {
                x = 255 - (-x & (PERMUTATION_TABLE_SIZE - 1));
            }
            if (y > 0) {
                y = y & (PERMUTATION_TABLE_SIZE - 1);
            } else {
                y = 255 - (-y & (PERMUTATION_TABLE_SIZE - 1));
            }
            if (z > 0) {
                z = z & (PERMUTATION_TABLE_SIZE - 1);
            } else {
                z = 255 - (-z & (PERMUTATION_TABLE_SIZE - 1));
            }
            if (t > 0) {
                t = t & (PERMUTATION_TABLE_SIZE - 1);
            } else {
                t = 255 - (-t & (PERMUTATION_TABLE_SIZE - 1));
            }
            return VALUES_TABLE[PERMUTATION_TABLE[x + PERMUTATION_TABLE[y + PERMUTATION_TABLE[z + PERMUTATION_TABLE[t]]]]];
        }
    }
}
